﻿using System;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using Paladyne.FileSearch.Core;

namespace Paladyne.FileSearch.XmlSearchModule
{
    /// <summary>
    /// XML (.xml) files search module
    /// </summary>
    [Export(typeof (ISearchModule))]
    public class XmlSearchModuleViewModel : ISearchModule
    {
        private readonly ITextCompareStrategy _compareStrategy;

        #region Constructors

        public XmlSearchModuleViewModel(ITextCompareStrategy compareStrategy)
        {
            _compareStrategy = compareStrategy;
            SearchNodeName = String.Empty;
            SearchNodeValue = String.Empty;
        }

        public XmlSearchModuleViewModel()
            : this(new DefaultTextCompareStrategy())
        {
        }

        #endregion

        #region Implementation of ISearchModule

        public string Name
        {
            get { return "Xml searcher"; }
        }

        public string Extension
        {
            get { return ".xml"; }
        }

        /// <summary>
        /// Check if file satisfies module's conditions (contains Node with specified name and/or with Value containing search string)
        /// </summary>
        /// <param name="fileInfo"></param>
        /// <returns></returns>
        public bool Check(FileInfo fileInfo)
        {
            if (String.IsNullOrWhiteSpace(SearchNodeName) && String.IsNullOrWhiteSpace(SearchNodeValue))
                return true;

            try
            {
                XDocument doc = XDocument.Load(fileInfo.FullName);
                return RecursiveCheck(doc.Root);
            }
            catch (Exception e)
            {
                // if XML file is invalid or cannot be opened, just skip it
                if (e is FileNotFoundException || e is UnauthorizedAccessException || e is XmlException)
                    return false;
                throw;
            }
        }

        /// <summary>
        /// Recursively search in XML file for elements which meet the conditions
        /// </summary>
        /// <param name="element"></param>
        /// <returns></returns>
        private bool RecursiveCheck(XElement element)
        {
            bool nodeNamesAreEqual = _compareStrategy.AreEqual(element.Name.ToString(), SearchNodeName.Trim(),
                                                               IgnoreCase);
            bool nodeIsLeafAndValueContainsSearchString = !element.HasElements &&
                                                          _compareStrategy.Contains(element.Value,
                                                                                    SearchNodeValue.Trim(), IgnoreCase);

            if (String.IsNullOrWhiteSpace(SearchNodeValue) && nodeNamesAreEqual)
                return true;

            if (String.IsNullOrWhiteSpace(SearchNodeName) && nodeIsLeafAndValueContainsSearchString)
                return true;

            if (nodeNamesAreEqual && nodeIsLeafAndValueContainsSearchString)
                return true;

            if (element.HasElements)
                return element.Elements().Any(RecursiveCheck);

            return false;
        }

        #endregion

        public string SearchNodeName { get; set; }
        public string SearchNodeValue { get; set; }
        public bool IgnoreCase { get; set; }
    }
}